package ws.client;

import io.netty.buffer.ByteBufUtil;
import io.netty.channel.*;
import io.netty.handler.codec.http.DefaultHttpHeaders;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.websocketx.*;
import io.netty.util.CharsetUtil;
import lombok.extern.slf4j.Slf4j;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.net.URISyntaxException;
import java.util.zip.GZIPInputStream;

/**
 * @author patrick
 * @date 2021/11/20 18
 */
@Slf4j
@ChannelHandler.Sharable
public class WebSocketClientHandler extends SimpleChannelInboundHandler {
    WebSocketClientHandshaker handshake;
    ChannelPromise handshakePromise;

    private final WebSocketClient webSocketClient;

    public WebSocketClientHandler(WebSocketClient webSocketClient) {
        this.webSocketClient = webSocketClient;
    }

    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        handshakePromise = ctx.newPromise();
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
        Channel ch = ctx.channel();
        FullHttpResponse response;
        if (!this.handshake.isHandshakeComplete()) {
            try {
                response = (FullHttpResponse) msg;
                //握手协议返回，设置结束握手
                this.handshake.finishHandshake(ch, response);
                //设置成功
                handshakePromise.setSuccess();

                System.out.println("handshake success");

                webSocketClient.finish();
            } catch (WebSocketHandshakeException e) {
                log.error(e.getMessage(), e);
                handshakePromise.setFailure(e);
            }
        } else if (msg instanceof FullHttpResponse) {
            response = (FullHttpResponse) msg;
            throw new IllegalStateException("Unexpected FullHttpResponse (getStatus=" + response.status() + ", content=" + response.content().toString(CharsetUtil.UTF_8) + ')');
        } else {
            WebSocketFrame frame = (WebSocketFrame) msg;
            if (frame instanceof TextWebSocketFrame textWebSocketFrame) {
                String msgStr = textWebSocketFrame.text();
                webSocketClient.messageInHandler().handle(msgStr);
            } else if (frame instanceof BinaryWebSocketFrame binaryWebSocketFrame) {
                String msgStr = new String(uncompress(ByteBufUtil.getBytes(binaryWebSocketFrame.content())));
                webSocketClient.messageInHandler().handle(msgStr);
            } else if (frame instanceof CloseWebSocketFrame) {
                System.out.println("receive close frame");
                ch.close();
            }
        }
    }

    private byte[] uncompress(byte[] bytes) {
        if (bytes == null || bytes.length == 0) {
            return null;
        }
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        ByteArrayInputStream in = new ByteArrayInputStream(bytes);
        try {
            GZIPInputStream ungzip = new GZIPInputStream(in);
            byte[] buffer = new byte[256];
            int n;
            while ((n = ungzip.read(buffer)) >= 0) {
                out.write(buffer, 0, n);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return out.toByteArray();
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws URISyntaxException {
        HttpHeaders httpHeaders = new DefaultHttpHeaders();
        handshake = WebSocketClientHandshakerFactory.newHandshaker(
                webSocketClient.url().toURI(),
                WebSocketVersion.V13,
                (String) null,
                true,
                httpHeaders);
        handshake.handshake(ctx.channel());
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        super.channelInactive(ctx);
        System.out.println("channelInactive");
    }
}